package com.motong.gongdan.task;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.motong.gongdan.config.wechat.WechatConfig;
import com.motong.gongdan.constant.CommonConstant;
import com.motong.gongdan.entity.*;
import com.motong.gongdan.service.*;
import com.motong.gongdan.util.*;
import com.motong.gongdan.vo.resp.ProductionResp;
import com.motong.gongdan.vo.system.LampRequestNotice.query.WXNoticeListQuery;
import com.motong.gongdan.vo.system.LampRequestNotice.vo.LampRequestNoticeWX;
import com.motong.gongdan.vo.system.common.query.WXMsgTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author:fff
 * @time:2022/3/24
 */
@Component
@EnableScheduling
@Slf4j
public class ProductionTask {
    @Resource
    WorkOrderService workOrderService;
    @Resource
    MissionService missionService;
    @Resource
    ProcessesService processesService;

    @Resource
    private FactoryService factoryService;

    @Resource
    private DeviceInfoService deviceInfoService;

    @Resource
    private DeviceStatusService deviceStatusService;

    @Resource
    private ExceptionInfoService exceptionInfoService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private LampRequestNoticeService lampRequestNoticeService;

    @Resource
    private WechatConfig wechatConfig;


    @Scheduled(cron = "0 0 0 * * ?")
    public void updateProduction() {

        List<Factory> list = factoryService.list();
        list.forEach(i -> {
            JSONObject old = (JSONObject) RedisUtil.get("production:day:" + i.getId());
            if (!old.isEmpty()) {
                RedisUtil.set("production:yesterday:" + i.getId(), old);
                JSONObject month = (JSONObject) RedisUtil.get("production:month:" + i.getId());
                if (month.isEmpty()) {
                    RedisUtil.set("production:month:" + i.getId(), old);
                }
            }
        });

    }

    @Scheduled(cron = "0 */1 * * * ?")
    public void day() {
        LoginUtil.setIgnoreFactory(true);
        List<Factory> factoryList = factoryService.list();
        try {
            for (Factory factory : factoryList) {
                ProductionResp pr = new ProductionResp();
                pr.setAverageProduce(BigDecimal.ZERO);
                pr.setAllCompletedNumber(BigDecimal.ZERO);
                pr.setBadProductRate(BigDecimal.ZERO);
                pr.setAllPlanedNumber(BigDecimal.ZERO);
                pr.setWorkOrderNumber(BigDecimal.ZERO);
                List<WorkOrder> list = workOrderService.list(new LambdaQueryWrapper<WorkOrder>().in(WorkOrder::getState, Arrays.asList(0, 1)).eq(WorkOrder::getFactoryId, factory.getId()));
                if (!CollectionUtils.isEmpty(list)) {
                    pr.setNotFinished(BigDecimal.valueOf(list.stream().filter(a -> a.getEndTime() != null && !TimeUtil.afterNow(a.getEndTime())).count()));
                    List<WorkOrder> dayList = list.stream().filter(day -> day.getState() == 1).collect(Collectors.toList());
                    getMission(pr, dayList, factory.getId());

                }else{
                    pr.setNotFinished(BigDecimal.ZERO);
                }
                JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(pr));
                RedisUtil.set("production:day:" + factory.getId(), jsonObject);

                List<WorkOrder> monthList = workOrderService.list(new QueryWrapper<WorkOrder>()
                        .between("start_time", TimeUtil.getTimesMonthmorning(), TimeUtil.getTimesMonthnight())
                        .eq("factory_id", factory.getId()));
                getMission(pr, monthList, factory.getId());
                JSONObject jsonObjectMonth = JSONObject.parseObject(JSON.toJSONString(pr));
                RedisUtil.set("production:month:" + factory.getId(), jsonObjectMonth);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            LoginUtil.removeIgnoreFactory();
        }
    }

    private void getMission(ProductionResp pr, List<WorkOrder> monthList, Integer factoryId) {
        if (!CollectionUtils.isEmpty(monthList)) {
            //pr.setAllPlanedNumber(BigDecimal.valueOf(monthList.stream().mapToDouble(WorkOrder::getPlanedNumber).sum()));
            pr.setAllPlanedNumber(monthList.stream().map(WorkOrder::getPlanedNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum));
            pr.setWorkOrderNumber(BigDecimal.valueOf(monthList.size()));
            List<String> workOrderCodeList = monthList.stream().map(wo -> wo.getCode()).collect(Collectors.toList());
            List<Mission> mission = missionService.list(new QueryWrapper<Mission>()
                    .in("work_order_code", workOrderCodeList).eq("last", 0).eq("factory_id", factoryId));
            //pr.setAllCompletedNumber(mission.stream().mapToInt(Mission::getGoodProductNumber).sum());
            pr.setAllCompletedNumber(mission.stream().map(Mission::getGoodProductNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum));
            //Integer badProductNumber = mission.stream().mapToInt(Mission::getBadProductNumber).sum();
            BigDecimal badProductNumber = mission.stream().map(Mission::getBadProductNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum);

            //Integer planedNumber = mission.stream().mapToInt(Mission::getPlanedNumber).sum();
            BigDecimal planedNumber = mission.stream().map(Mission::getPlanedNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum);
            if (badProductNumber.compareTo(BigDecimal.ZERO) != 0) {
                pr.setBadProductRate((badProductNumber
                        .divide(planedNumber, 4, BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(100))));
            }
        }
    }

    //    @Scheduled(cron = "0 */1 * * * ?")
    @Scheduled(cron = "0/20 * * * * ?")
    public void dataNumber() {
        LoginUtil.setIgnoreFactory(true);
        try {
            List<Factory> list = factoryService.list();
            list.forEach(factory -> {
                Map<String, Object> map = new HashMap<>();
                List<WorkOrder> workOrderList = workOrderService.list(new QueryWrapper<WorkOrder>().eq("state", 1).eq("factory_id", factory.getId()));
                if (!CollectionUtils.isEmpty(workOrderList)) {
                    // 执行中工单计划总数
                    map.put("workOrderPlanedNumber", String.valueOf(workOrderList.stream().map(WorkOrder::getPlanedNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum)));
                    // 执行中工单数量
                    map.put("workOrderNumber", String.valueOf(workOrderList.size()));
                    // 执行中工单延期数量
                    map.put("workOrderDelayNumber", String.valueOf((int) workOrderList.stream().filter(w -> w.getEndTime() != null && !TimeUtil.afterNow(w.getEndTime())).count()));
                    List<WorkOrder> dayFinished = workOrderService.list(new QueryWrapper<WorkOrder>().eq("state", 2).eq("factory_id", factory.getId())
                            .like("end_time", TimeUtil.getCurrentDateString(TimeUtil.DEFAULT_DAY_FORMAT)));
                    if (!CollectionUtils.isEmpty(dayFinished)) {
                        // 已完成工单数量
                        map.put("dayFinishedWONumber", String.valueOf(dayFinished.size()));
                        // 已完成工单生产数量
                        map.put("dayProduceWONumber", String.valueOf(dayFinished.stream().map(WorkOrder::getGoodProductNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum)));
                    } else {
                        map.put("dayFinishedWONumber", String.valueOf(0));
                        map.put("dayProduceWONumber", String.valueOf(0));
                    }
                    List<String> workOrderCodes = workOrderList.stream().map(WorkOrder::getCode).collect(Collectors.toList());
                    List<Mission> missionList = missionService.list(new QueryWrapper<Mission>().in("work_order_code", workOrderCodes).eq("factory_id", factory.getId()));
                    if (!CollectionUtils.isEmpty(missionList)) {
                        // 进行中工单(任务数量)
                        map.put("missionNumber", String.valueOf(missionList.size()));
                        // 进行中(任务计划数量)
                        map.put("missionPlanedNumber", String.valueOf(missionList.stream().map(Mission::getPlanedNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum)));
                        List<String> delay = workOrderList.stream().filter(
                                d -> d.getEndTime() != null && !TimeUtil.afterNow(d.getEndTime())).map(WorkOrder::getCode).collect(Collectors.toList());
                        if (!CollectionUtils.isEmpty(delay)) {
                            List<Mission> delayMission = missionService.list(new QueryWrapper<Mission>().in("work_order_code", delay).eq("factory_id", factory.getId()));
                            // 任务延期数量
                            map.put("missionDelayNumber", String.valueOf(delayMission.size()));
                        } else {
                            map.put("missionDelayNumber", String.valueOf(0));
                        }
                        List<Mission> dayMission = missionService.list(new QueryWrapper<Mission>().eq("process_state", 2).eq("factory_id", factory.getId())
                                .like("actual_end_time", TimeUtil.getCurrentDateString(TimeUtil.DEFAULT_DAY_FORMAT)));
                        if (!CollectionUtils.isEmpty(dayMission)) {
                            BigDecimal planedNumber = dayMission.stream().map(Mission::getPlanedNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum);
                            // 今日完成任务数
                            map.put("dayFinishedMNumber", String.valueOf(dayMission.size()));
                            BigDecimal badProduct = dayMission.stream().map(Mission::getBadProductNumber).reduce(BigDecimal.ZERO, BigDecimalSum::sum);
                            BigDecimal rate = badProduct.divide(planedNumber, 4, BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(100));
                            // 今日不良品率
                            map.put("dayBadProductRate", String.valueOf(rate));
                        } else {
                            map.put("dayFinishedMNumber", String.valueOf(0));
                            map.put("dayBadProductRate", String.valueOf(0));
                        }
                    } else {
                        map.put("missionNumber", String.valueOf(0));
                        map.put("missionPlanedNumber", String.valueOf(0));
                        map.put("missionDelayNumber", String.valueOf(0));
                        map.put("dayFinishedMNumber", String.valueOf(0));
                    }
                } else {
                    map.put("workOrderPlanedNumber", String.valueOf(0));
                    map.put("workOrderNumber", String.valueOf(0));
                    map.put("workOrderDelayNumber", String.valueOf(0));
                    map.put("dayFinishedWONumber", String.valueOf(0));
                    map.put("dayProduceWONumber", String.valueOf(0));
                    map.put("dayBadProductRate", String.valueOf(0));
                    map.put("missionNumber", String.valueOf(0));
                    map.put("missionPlanedNumber", String.valueOf(0));
                    map.put("missionDelayNumber", String.valueOf(0));
                    map.put("dayFinishedMNumber", String.valueOf(0));
                }
                if (map != null && !map.isEmpty()) {
                    RedisUtil.set("data:home:num:" + factory.getId(), JSONObject.parseObject(JSON.toJSONString(map)));
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            LoginUtil.removeIgnoreFactory();
        }


    }


    //数据看板
//    @Scheduled(cron = "0 */10 * * * ?")
//    public void dataBoard() throws ParseException {
//        List<Factory> list = factoryService.list();
//        for (Factory factory : list) {
//            try {
//                LoginUtil.removeIgnoreFactory();
//                LoginUtil.setLoginUserFactory(factory.getId());
//                //今天
//                Date startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(TimeUtil.getDayEarlyTime());
//                Date endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(TimeUtil.getDayLateTime());
//                H5DataBoardResp todayResp = workOrderService.getBaseMapper().getDataBoard(startTime, endTime);
//                if (todayResp.getAllBadProductNum() != null && todayResp.getAllPlanProductNum() != null) {
//                    String probability = todayResp.getAllBadProductNum().divide(todayResp.getAllPlanProductNum(), 2, BigDecimal.ROUND_HALF_UP)
//                            .multiply(BigDecimal.valueOf(100)) + "%";
//                    todayResp.setProbability(probability);
//                }
//                JSONObject jsonObjectDay = JSON.parseObject(JSON.toJSONString(todayResp));
//                RedisUtil.set("dataBoard:day:"+factory.getId(), jsonObjectDay);
//                //昨天
//                //获取前一天的日期
//                H5DataBoardResp beforeDayResp = workOrderService.getBaseMapper().getDataBoard(TimeUtil.getBeforeDayStartTime(), TimeUtil.getBeforeDayEndTime());
//                if (beforeDayResp.getAllBadProductNum() != null && beforeDayResp.getAllPlanProductNum() != null) {
//                    String probability = beforeDayResp.getAllBadProductNum().divide(beforeDayResp.getAllPlanProductNum(), 2, BigDecimal.ROUND_HALF_UP)
//                            .multiply(BigDecimal.valueOf(100)) + "%";
//                    beforeDayResp.setProbability(probability);
//                }
//                JSONObject jsonObjectBeforeDayResp = (JSONObject) JSONObject.toJSON(beforeDayResp);
//                RedisUtil.set("dataBoard:yesterday:"+factory.getId(), jsonObjectBeforeDayResp);
//                //前七天
//                H5DataBoardResp beforeWeekResp = workOrderService.getBaseMapper().getDataBoard(TimeUtil.getBeforeWeekStartTime(), TimeUtil.getBeforeWeekEndTime());
//                if (beforeWeekResp.getAllBadProductNum() != null && beforeWeekResp.getAllPlanProductNum() != null) {
//                    String probability = beforeWeekResp.getAllBadProductNum().divide(beforeWeekResp.getAllPlanProductNum(), 2, BigDecimal.ROUND_HALF_UP)
//                            .multiply(BigDecimal.valueOf(100)) + "%";
//                    beforeWeekResp.setProbability(probability);
//                }
//                JSONObject jsonObjectBeforeWeekResp = (JSONObject) JSONObject.toJSON(beforeWeekResp);
//                RedisUtil.set("dataBoard:lastWeek:"+factory.getId(), jsonObjectBeforeWeekResp);
//            }catch (Exception e) {
//                e.printStackTrace();
//            }finally {
//                LoginUtil.removeIgnoreFactory();
//            }
//
//        }
//    }

    /**
     * 每分钟查询一次异常上报
     */
    @Scheduled(cron = "0/5 * * * * ? ")
    public void exceptionReport() {
        synchronized (ProductionTask.class){
            Date date = new Date();

            LoginUtil.setIgnoreFactory(true);
            LambdaQueryWrapper<DeviceStatus> exceptionInfoListQW = new LambdaQueryWrapper<DeviceStatus>()
                    .in(DeviceStatus::getStatus, 2, 3).eq(DeviceStatus::getReportException, 0).isNull(DeviceStatus::getDuration);
            List<DeviceStatus> deviceStatusList = deviceStatusService.list(exceptionInfoListQW);
            Set<Long> deviceIds = deviceStatusList.stream().map(DeviceStatus::getDeviceId).collect(Collectors.toSet());
            HashMap<Long, DeviceInfo> deviceIdDataMap = new HashMap<>(deviceIds.size());
            if (!CollectionUtils.isEmpty(deviceIds)) {
                deviceInfoService.listByIds(deviceIds).forEach(i -> deviceIdDataMap.put(i.getId(), i));
            }
            LoginUtil.setIgnoreFactory(false);
            deviceStatusList.stream().collect(Collectors.groupingBy(DeviceStatus::getFactoryId)).forEach((k, v) -> {
                LoginUtil.setLoginUserFactory(k);
                //需要更新上报状态的数据
                List<DeviceStatus> updateDeviceStatuses = new ArrayList<>();
                List<ExceptionInfo> saveExceptionInfos = new ArrayList<>();
                //超时上报时间
                Object yellowMinuteO = redisTemplate.opsForHash().get(CommonConstant.EXCEPTION_TIMEOUT_HASH_KEY, "0:".concat(k.toString()));
                Object redMinuteO = redisTemplate.opsForHash().get(CommonConstant.EXCEPTION_TIMEOUT_HASH_KEY, "1:".concat(k.toString()));
                Long yellowMinute = Objects.isNull(yellowMinuteO) ? CommonConstant.EXCEPTION_TIMEOUT_DEFAULT_MINUTE : Convert.toLong(yellowMinuteO);
                Long redMinute = Objects.isNull(redMinuteO) ? CommonConstant.EXCEPTION_TIMEOUT_DEFAULT_MINUTE : Convert.toLong(redMinuteO);
                // 判断状态生成异常信息
                if (!CollectionUtils.isEmpty(v)) {
                    v.forEach(i -> {
                        if (DateUtil.between(i.getCreateTime(), date, DateUnit.MINUTE) > (i.getStatus() == 2 ? yellowMinute : redMinute)) {
                            DeviceInfo deviceInfo = deviceIdDataMap.get(i.getDeviceId());
                            if (Objects.nonNull(deviceInfo)) {
                                ExceptionInfo exceptionInfo = new ExceptionInfo().setCreateTime(new Date()).setDeviceId(deviceInfo.getId())
                                        .setGroupId(deviceInfo.getGroupId()).setMissionId(deviceInfo.getMissionId()).setLampColor(i.getStatus() == 2 ? 0 : 1);
                                saveExceptionInfos.add(exceptionInfo);
                            }
                            i.setReportException(1);
                            updateDeviceStatuses.add(i);
                        }
                    });
                }
                if (!CollectionUtils.isEmpty(saveExceptionInfos)) {
                    exceptionInfoService.saveBatch(saveExceptionInfos);
                }
                if (!CollectionUtils.isEmpty(updateDeviceStatuses)) {
                    deviceStatusService.updateBatchById(updateDeviceStatuses);
                }
                LoginUtil.setLoginUserFactory(null);
            });
            //联络等请求微信消息呼叫
            lampWXNotice(date);
        }
    }


    /**
     * 联络灯请求微信消息呼叫
     * @param currentDate  当前时间
     */
    public void lampWXNotice(Date currentDate) {
        LoginUtil.setIgnoreFactory(true);
        List<LampRequestNoticeWX> notice = lampRequestNoticeService.getBaseMapper().getWXNoticeList(new WXNoticeListQuery().setCurrentDate(currentDate));
        if (!CollectionUtils.isEmpty(notice)) {
            String accessToken = WeChatUtil.getAccessToken(wechatConfig);
            Map<Integer, List<LampRequestNoticeWX>> factoryIdNoticeMap = notice.stream().collect(Collectors.groupingBy(LampRequestNoticeWX::getFactoryId));
            factoryIdNoticeMap.forEach((k, v) -> {
                LoginUtil.setIgnoreFactory(false);
                LoginUtil.setLoginUserFactory(k);
                List<LampRequestNotice> lampRequestNotices = new ArrayList<>(v.size());
                v.forEach(i -> {
                    try {
                        //发送微信通知
                        WXMsgTemplate wxMsgTemplate = new WXMsgTemplate().setTouser(i.getOpenId()).setTemplate_id("0bq5dWCeOiyAmL9rqodTmV5gwkCGeqW9PXkW0O3Ss-E");
                        Map<String, Object> param = new HashMap<>();
                        param.put("first", Collections.singletonMap("value", "联络灯呼叫"));
                        param.put("keyword1", Collections.singletonMap("value", ""));
                        param.put("keyword2", Collections.singletonMap("value", i.getNoticeText()));
                        param.put("keyword3", Collections.singletonMap("value", new DateTime(i.getNoticeTime()).toString()));
                        param.put("remark", Collections.singletonMap("value", ""));
                        wxMsgTemplate.setData(param);
                        WeChatUtil.sendMsgByTemplate(wxMsgTemplate, accessToken);
                        i.setRoundNum(i.getRoundNum() - 1);
                        if (i.getRoundNum() == 0) {
                            i.setInfoStatus(1);
                        } else {
                            i.setNoticeTime(DateUtil.offsetMinute(currentDate, i.getIntervalTime()));
                        }
                        LampRequestNotice lampRequestNotice = BeanUtil.copyProperties(i, LampRequestNotice.class);
                        lampRequestNotices.add(lampRequestNotice);
                    } catch (Exception e) {
                        log.error("联络等请求微信消息呼叫发生错误!");
                    }
                });
                if (!CollectionUtils.isEmpty(lampRequestNotices)) {
                    lampRequestNoticeService.updateBatchById(lampRequestNotices);
                }
                LoginUtil.setLoginUserFactory(null);
            });
        }

    }

}
